#region Using
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using JigLibX.Physics;
using JigLibX.Collision;
using Debug;
#endregion Using

public enum GameState
{
    MultiplayerGame,
    DebugGame,
    MenuScreen
}

public enum MultiplayerType
{
    SystemLink,
    LIVE
}

public enum NetworkDataType
{
    CountDown,
    CarType,
    SpawnPosition,
    PlayerMove
}

namespace LloydGame
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region Class Fields
        protected bool m_debug;
        protected GraphicsDeviceManager m_graphics;
        protected SpriteBatch m_spriteBatch;

        protected GameWorld m_gameWorld;
        protected PhysicsSystem m_physSystem;
        protected GameTime m_gameTime;
        protected bool m_gameInitiated;

        protected Camera m_camera;
        protected UserInput m_userInput;
        protected MapEditor m_mapEditor;

        protected bool m_wireframe;

        // Sound
        protected Cue currentSong;
        protected AudioEngine m_audioEngine;
        protected SoundBank m_soundBank;
        protected WaveBank m_waveBank;

        // Editor
        protected frmEditor m_formEditor;

        // Menu/Screen system
        protected SpriteFont m_menuFontHuge;
        protected SpriteFont m_menuFontBig;
        protected SpriteFont m_menuFontNormal;
        protected SpriteFont m_menuFontSmall;
        protected SpriteFont m_menuFontTiny;
        protected GameScreen m_screenMainMenu;

        // Network
        protected NetworkSession m_networkSession;
        protected PacketWriter m_packetWriter = new PacketWriter();
        protected PacketReader m_packetReader = new PacketReader();
        protected int m_networkMaxPlayers = 4;

        // HUD / GUI
        protected bool m_show_gamer_list;
        protected Texture2D m_gamer_list_bg;
        protected Texture2D m_gamer_finallist_bg;
        protected List<ScreenFlash> m_screen_flashes;
        protected Texture2D m_flash_tex_count_go;
        protected Texture2D m_flash_tex_count_one;
        protected Texture2D m_flash_tex_count_two;
        protected Texture2D m_flash_tex_count_three;
        protected Texture2D m_blank_texture;
        protected Dictionary<String, Texture2D> m_flash_pu;
        protected byte m_winner_id;
        protected Texture2D m_winner_icon;
        protected Texture2D m_powerup_icons;
        public bool reverse_powerup;

        // Network host count down data
        protected int m_init_countdown_id;
        protected float m_init_countdown_time;

        // Network host car type information
        protected Dictionary<byte,int> m_car_types;

        // Settings
        protected bool m_setting_motion_blur;
        protected bool m_setting_bloom;
        protected bool m_setting_ssao;
        protected bool m_setting_fps;
        protected bool m_setting_fullscreen;

        // Game state
        protected GameState m_gamestate;
        protected TimeSpan m_playtime;
        protected TimeSpan m_default_playtime;

        protected float m_timeStep = 0.0f;
        protected int m_iterationsMade = 0;

        // Easter eggs, HONK HONK~~
        protected List<Keys> m_ee_konamicode;
        protected List<Keys> m_ee_konamicode_correct;
        protected int m_ee_konamicode_i;
        protected bool m_ee_activated;
        protected Texture2D m_ee_sweetfish_tex;
        Random m_random;
        #endregion Class Fields

        #region Properties

        public Texture2D powerup_icons { get { return m_powerup_icons; } }

        public Dictionary<String, Texture2D> flash_pu
        {
            get
            {
                return m_flash_pu;
            }
        }

        public List<ScreenFlash> screen_flashes
        {
            get
            {
                return m_screen_flashes;
            }
        }

        public PhysicsSystem physSystem
        {
            get
            {
                return m_physSystem;
            }
        }

        public SoundBank soundBank
        {
            get
            {
                return m_soundBank;
            }
        }

        public Camera camera
        {
            get
            {
                return m_camera;
            }
        }

        public GraphicsDeviceManager graphics
        {
            get
            {
                return m_graphics;
            }
        }

        public UserInput userInput
        {
            get
            {
                return m_userInput;
            }
        }

        public GameWorld gameWorld
        {
            get
            {
                return m_gameWorld;
            }
        }

        public GameTime gameTime
        {
            get { return m_gameTime; }
        }

        public Random random
        {
            get
            {
                return m_random;
            }
        }

        public bool isDebug
        {
            get { return m_debug; }
        }

        public bool settingGFXMotionBlur
        {
            get { return m_setting_motion_blur; }
            set { m_setting_motion_blur = value; }
        }

        public bool settingGFXBloom
        {
            get { return m_setting_bloom; }
            set { m_setting_bloom = value; }
        }

        public bool settingGFXSSAO
        {
            get { return m_setting_ssao; }
            set { m_setting_ssao = value; }
        }

        public bool settingGFXFPS
        {
            get { return m_setting_fps; }
            set { m_setting_fps = value; }
        }

        public bool settingGFXFS
        {
            get { return m_setting_fullscreen; }
            set { m_setting_fullscreen = value; }
        }

        public bool ee { get { return m_ee_activated; } }

        public int iterationsMade { get { return m_iterationsMade; } }

        public bool gameInitiated { get { return m_gameInitiated; } }

        public SpriteFont menuFontBig { get { return m_menuFontBig; } }
        public SpriteFont menuFontNormal { get { return m_menuFontNormal; } }
        public SpriteFont menuFontSmall { get { return m_menuFontSmall; } }
        public SpriteFont menuFontTiny { get { return m_menuFontTiny; } }

        public NetworkSession networkSession { get { return m_networkSession; } set { m_networkSession = value; } }
        public int networkMaxPlayers { get { return m_networkMaxPlayers; } set { m_networkMaxPlayers = value; } }
        #endregion Properties

        public Game1()
        {
            m_graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Enable multi sampling for AA
            m_graphics.PreferMultiSampling = true;
            m_graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
            m_graphics.ApplyChanges();

            // Enable wireframe in debug mode?
            m_wireframe = false;
            m_debug = true;

            // Load config file
            LoadConfig();

            this.graphics.IsFullScreen = settingGFXFS;

            // Set resolution?
            //m_graphics.PreferredBackBufferWidth = 400;
            //m_graphics.PreferredBackBufferHeight = 300;

            // Set default game time
            m_default_playtime = TimeSpan.FromMinutes(5.0);

            // Set game state to menu (we want to start in the main menu!)
            m_gamestate = GameState.MenuScreen;
            //m_gamestate = GameState.DebugGame;

            // Network
            Components.Add(new GamerServicesComponent(this));
            m_car_types = new Dictionary<byte, int>();

            // Screen Flashes
            m_screen_flashes = new List<ScreenFlash>();

            // Easter Eggs
            m_ee_konamicode = new List<Keys>();
            m_ee_konamicode_correct = new List<Keys>();
            m_ee_konamicode_correct.Add(Keys.Up);
            m_ee_konamicode_correct.Add(Keys.Up);
            m_ee_konamicode_correct.Add(Keys.Down);
            m_ee_konamicode_correct.Add(Keys.Down);
            m_ee_konamicode_correct.Add(Keys.Left);
            m_ee_konamicode_correct.Add(Keys.Right);
            m_ee_konamicode_correct.Add(Keys.Left);
            m_ee_konamicode_correct.Add(Keys.Right);
            m_ee_konamicode_correct.Add(Keys.B);
            m_ee_konamicode_correct.Add(Keys.A);
            m_ee_konamicode_correct.Add(Keys.Enter);
            m_ee_konamicode_i = 0;
            m_ee_activated = false;

            m_random = new Random();

            this.IsMouseVisible = true;
        }

        // Enable AA
        private void graphics_PreparingDeviceSettings(object p_sender, PreparingDeviceSettingsEventArgs p_e)
        {
            GraphicsAdapter adapter = p_e.GraphicsDeviceInformation.Adapter;
            SurfaceFormat format = adapter.CurrentDisplayMode.Format;

            // Array with AA settings to try out
            MultiSampleType[] mst = { MultiSampleType.None };

            for (int i = 0; i < mst.Length; ++i)
            {
                if (adapter.CheckDeviceMultiSampleType(DeviceType.Hardware, format, false, mst[i]))
                {
                    p_e.GraphicsDeviceInformation.PresentationParameters.MultiSampleQuality = 0;
                    p_e.GraphicsDeviceInformation.PresentationParameters.MultiSampleType = mst[i];
                    break;
                }
            }

            return;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            /*m_camera = new Camera(m_graphics,
                                  new Vector3(0.0f, 30.0f, 40.0f),
                                  Vector3.Zero,
                                  Camera.cameraState.FREE,
                                  null);
             */
            /*
            graphics.SynchronizeWithVerticalRetrace = true;
            IsFixedTimeStep = true;

            m_physSystem = new PhysicsSystem();

            graphics.SynchronizeWithVerticalRetrace = false;
            IsFixedTimeStep = false;

            m_physSystem.CollisionSystem = new CollisionSystemSAP();

            FrameRateCounter physStats = new FrameRateCounter(this, m_physSystem);
            physStats.DrawOrder = 2;
            Components.Add(physStats);
            */
            m_show_gamer_list = true;
            base.Initialize();
        }

        public void RestoreRenderState()
        {
            m_graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;
            m_graphics.GraphicsDevice.RenderState.AlphaTestEnable = true;
            m_graphics.GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            m_graphics.GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
            m_graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;
        }

        public void SaveConfig()
        {
            try
            {
                using (StreamWriter sw = new StreamWriter("GameContent\\config.txt"))
                {
                    sw.WriteLine("gfx_motionblur " + (m_setting_motion_blur ? "true" : "false"));
                    sw.WriteLine("gfx_bloom " + (m_setting_bloom ? "true" : "false"));
                    sw.WriteLine("gfx_ssao " + (m_setting_ssao ? "true" : "false"));
                    sw.WriteLine("gfx_fps " + (m_setting_fps ? "true" : "false"));
                    sw.WriteLine("gfx_fullscreen " + (m_setting_fullscreen ? "true" : "false"));
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Config file could not be written:");
                Console.WriteLine(e.Message);
            }
        }

        public void LoadConfig()
        {
            char[] charSeparators = new char[] { ' ', '\t' };

            try
            {
                using (StreamReader sr = new StreamReader("GameContent\\config.txt"))
                {
                    String line;

                    // read the file line by line
                    while ((line = sr.ReadLine()) != null)
                    {
                        if (line.StartsWith("#")) continue; // Skip comments

                        string[] tokens = line.Split(charSeparators, System.StringSplitOptions.RemoveEmptyEntries);

                        if (tokens.Length == 0) continue; // Skip empty rows

                        if (tokens[0] == "gfx_motionblur")
                            m_setting_motion_blur = (tokens[1] == "true") ? true : false;
                        else if (tokens[0] == "gfx_bloom")
                            m_setting_bloom = (tokens[1] == "true") ? true : false;
                        else if (tokens[0] == "gfx_ssao")
                            m_setting_ssao = (tokens[1] == "true") ? true : false;
                        else if (tokens[0] == "gfx_fps")
                            m_setting_fps = (tokens[1] == "true") ? true : false;
                        else if (tokens[0] == "gfx_fullscreen")
                            m_setting_fullscreen = (tokens[1] == "true") ? true : false;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Config file could not be read, or wasn't properly formated:");
                Console.WriteLine(e.Message);
            }
        }
        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            m_spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load fonts
            m_menuFontHuge = Content.Load<SpriteFont>("DaysHuge");
            m_menuFontBig = Content.Load<SpriteFont>("DaysBig");
            m_menuFontNormal = Content.Load<SpriteFont>("DaysNormal");
            m_menuFontSmall = Content.Load<SpriteFont>("DaysSmall");
            m_menuFontTiny = Content.Load<SpriteFont>("DaysTiny");
            m_screenMainMenu = new ScreenMainMenu(this);

            // Load sprite / textures
            m_gamer_list_bg = Texture2D.FromFile(GraphicsDevice, "Content\\hud_gamerlist_back.png");
            m_gamer_finallist_bg = Texture2D.FromFile(GraphicsDevice, "Content\\hud_finalgamerlist_bg.png");
            m_flash_tex_count_go = Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_go.png");
            m_flash_tex_count_one = Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_one.png");
            m_flash_tex_count_two = Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_two.png");
            m_flash_tex_count_three = Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_three.png");
            m_winner_icon = Texture2D.FromFile(GraphicsDevice, "Content\\hud_winner_icon.png");
            m_blank_texture = Texture2D.FromFile(GraphicsDevice, "Content\\blank.png");
            m_powerup_icons = Texture2D.FromFile(GraphicsDevice, "Content\\powerup_icons.png");

            // Load powerup flash textures
            m_flash_pu = new Dictionary<string, Texture2D>();
            m_flash_pu.Add("NITRO", Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_pu_nitro.png"));
            m_flash_pu.Add("DOUBLEYOURPOINTS", Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_pu_double.png"));
            m_flash_pu.Add("DOUBLETHEIRPOINTS", Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_pu_doubletheir.png"));
            m_flash_pu.Add("PUNCH", Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_pu_punch.png"));
            m_flash_pu.Add("SLOW", Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_pu_slow.png"));
            m_flash_pu.Add("REVERSE_KEYS", Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_pu_reverse.png"));
            m_flash_pu.Add("LOW_FRICTION", Texture2D.FromFile(GraphicsDevice, "Content\\hud_flash_pu_friction.png"));

            //load sound
            m_audioEngine = new AudioEngine("Content\\Audio\\Lloyd_sound.xgs");
            m_waveBank = new WaveBank(m_audioEngine, "Content\\Audio\\Wave Bank.xwb");
            m_soundBank = new SoundBank(m_audioEngine, "Content\\Audio\\Sound Bank.xsb");

            // load world
            if (m_gamestate == GameState.DebugGame)
                InitDebugGame();
            /*
            createWorld("Content\\Maps\\level1.txt");
            m_formEditor = new frmEditor(m_gameWorld);
            m_formEditor.Show();
            m_mapEditor = new MapEditor(this, m_gameWorld, "test.txt");
            Components.Add(m_mapEditor);
            this.IsMouseVisible = true;
            GameObject targetObject = m_gameWorld.GetObject("Car");
            m_camera = new Camera(m_graphics,
                                  new Vector3(0.0f, 30.0f, 40.0f),
                                  Vector3.Zero,
                                  Camera.cameraState.FOLLOW,
                                  targetObject);
            */
            
        }

        /// <summary>
        /// InitDebugGame will be called in debug mode from the Main Menu screen.
        /// </summary>
        public void InitDebugGame()
        {
            m_spriteBatch = new SpriteBatch(GraphicsDevice);

            // load world
            createWorld("GameContent\\Maps\\level1.txt", true);

            // Init clock
            m_playtime = m_default_playtime;

            m_formEditor = new frmEditor(m_gameWorld);
            m_formEditor.Show();
            m_mapEditor = new MapEditor(this, m_gameWorld, "test.txt");
            Components.Add(m_mapEditor);
            this.IsMouseVisible = true;
            GameObject targetObject = m_gameWorld.GetObject("Car");
            
            /*m_camera = new Camera(m_graphics,
                                  new Vector3(0.0f, 30.0f, 40.0f),
                                  Vector3.Zero,
                                  Camera.cameraState.EDIT_MODE,
                                  targetObject);*/
            m_camera = new Camera(this, 
                                  new Vector3(16.94671f, 77.25067f, -110.0087f),
                                  new Vector3(-0.3810003f, 3.024002f, 0.0f),
                                  Camera.cameraState.EDIT_MODE,
                                  targetObject);
            
            // Update the game state
            m_gameInitiated = true;
            m_gamestate = GameState.DebugGame;
            
        }

        public void InitMultiplayerGame()
        {
            m_spriteBatch = new SpriteBatch(GraphicsDevice);

            // load world
            createWorld("GameContent\\Maps\\level1.txt", false);

            // Init clock
            m_playtime = m_default_playtime;

            this.IsMouseVisible = false;
            GameObject targetObject = m_gameWorld.GetObject("LocalCar");
            
            m_camera = new Camera(this, 
                                  new Vector3(16.94671f, 77.25067f, -110.0087f),
                                  new Vector3(-0.3810003f, 3.024002f, 0.0f),
                                  Camera.cameraState.FREE,
                                  targetObject);
            /*
            m_camera = new Camera(m_graphics,
                                  new Vector3(0.0f, 30.0f, 40.0f),
                                  Vector3.Zero,
                                  Camera.cameraState.FREE,
                                  targetObject);
             */

            // Update the game state
            m_winner_id = 255;
            m_gameInitiated = false;
            m_gamestate = GameState.MultiplayerGame;

        }

        //for easier debuging
        protected virtual void createWorld(String p_worldFile, bool p_debug)
        {
            // Moved here from Initialize()
            graphics.SynchronizeWithVerticalRetrace = true;
            IsFixedTimeStep = true;

            m_physSystem = new PhysicsSystem();

            graphics.SynchronizeWithVerticalRetrace = false;
            IsFixedTimeStep = false;

            m_physSystem.CollisionSystem = new CollisionSystemSAP();

            m_physSystem.EnableFreezing = true;
            m_physSystem.SolverType = PhysicsSystem.Solver.Normal;
            m_physSystem.CollisionSystem.UseSweepTests = true;

            m_physSystem.NumCollisionIterations = 5;
            m_physSystem.NumContactIterations = 15;
            m_physSystem.NumPenetrationRelaxtionTimesteps = 20;

            if (m_setting_fps)
            {
                FrameRateCounter physStats = new FrameRateCounter(this, m_physSystem);
                physStats.DrawOrder = 2;
                Components.Add(physStats);
            }

            // Create our gameworld from file..
            if (p_debug)
            {
                m_gameWorld = new DebugGameWorld(this, "GameContent\\Maps\\level1.txt", true, m_wireframe);
            }
            else
            {
                m_gameWorld = new GameWorld(this, "GameContent\\Maps\\level1.txt", false, false);
            }
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // GC takes care of most stuffs?
        }

        #region Network Methods
        public void NetworkListener()
        {
            Console.WriteLine("Something happened on the network session! :)");

        }

        public void NetworkGamerJoined(object sender, GamerJoinedEventArgs e)
        {
            // Debug, print joining players to output
            int gamerIndex = networkSession.AllGamers.IndexOf(e.Gamer);
            Console.Write("Gamer joined: ");
            Console.WriteLine(gamerIndex);
        }

        public void NetworkGamerLeft(object sender, GamerLeftEventArgs e)
        {
            // Debug, print parting players to output
            Console.WriteLine("Gamer left: " + e.Gamer.Gamertag);
        }

        public void NetworkReadData()
        {
            if (m_networkSession == null || m_networkSession.LocalGamers == null)
                return;

            LocalNetworkGamer t_gamer = m_networkSession.LocalGamers[0];

            // Keep reading as long as incoming packets are available.
            while (t_gamer.IsDataAvailable)
            {
                NetworkGamer sender;

                // Read a single packet from the network.
                t_gamer.ReceiveData(m_packetReader, out sender);

                String t_datatype = m_packetReader.ReadString();

                if (m_networkSession.SessionState == NetworkSessionState.Playing)
                {
                    if (!sender.IsLocal)
                    {
                        // Spawnpoint information has been recieved!
                        if (t_datatype == "SpawnPosition")
                        {
                            byte t_gamer_id = m_packetReader.ReadByte(); // gamer id
                            Vector3 t_spawnpos = m_packetReader.ReadVector3();
                            Vector3 t_spawnrot = m_packetReader.ReadVector3();
                            int t_car_type = m_packetReader.ReadInt32();

                            // Is this our position?
                            if (m_networkSession.LocalGamers[0].Id == t_gamer_id)
                            {
                                gameWorld.addLocalGamePlayer(t_car_type, t_spawnpos, t_spawnrot);

                                GameObject targetObject = m_gameWorld.GetObject("LocalCar");

                                m_camera = new Camera(this,
                                                      new Vector3(16.94671f, 77.25067f, -110.0087f),
                                                      new Vector3(-0.3810003f, 3.024002f, 0.0f),
                                                      Camera.cameraState.FOLLOW,
                                                      targetObject);
                            }
                            else
                            {
                                // This is a remote player, add to game world list
                                gameWorld.addRemoteGamePlayer(m_networkSession.FindGamerById(t_gamer_id), t_car_type, t_spawnpos, t_spawnrot);
                            }
                        }
                        // An updated player position has been recieved!
                        else if (t_datatype == "PlayerMove")
                        {
                            byte t_gamer_id = m_packetReader.ReadByte(); // gamer id
                            Vector3 t_position = m_packetReader.ReadVector3();
                            Vector3 t_velocity = m_packetReader.ReadVector3();
                            Matrix t_orientation = m_packetReader.ReadMatrix();

                            // Update position
                            gameWorld.updateRemotePlayer(t_gamer_id, t_position, t_velocity, t_orientation);

                            // If we are hosts, propagate the recieved information to the other clients
                            if (m_networkSession.IsHost)
                            {
                                List<Object> t_data_list = new List<object>();
                                t_data_list.Add(t_gamer_id); // Player id
                                t_data_list.Add(t_position); // position
                                t_data_list.Add(t_velocity); // velocity
                                t_data_list.Add(t_orientation); // orientation

                                NetworkSendData(NetworkDataType.PlayerMove, t_data_list, null);
                            }

                        }
                        // Server sent count down info!
                        else if (t_datatype == "CountDown")
                        {
                            int t_count_num = m_packetReader.ReadInt32(); // count number

                            ScreenFlash t_sf = null;
                            if (t_count_num == 0)
                            {
                                m_gameInitiated = true;
                                t_sf = new ScreenFlash(this, m_flash_tex_count_go, "go_beep", ScreenFlashType.ZoomWithTrailAndFade, 1.0f, 2.5f);
                            }
                            else if (t_count_num == 1)
                            {
                                t_sf = new ScreenFlash(this, m_flash_tex_count_one, "count_beep", ScreenFlashType.ZoomWithTrailAndFade, 1.0f, 2.5f);
                            }
                            else if (t_count_num == 2)
                            {
                                t_sf = new ScreenFlash(this, m_flash_tex_count_two, "count_beep", ScreenFlashType.ZoomWithTrailAndFade, 1.0f, 2.5f);
                            }
                            else if (t_count_num == 3)
                            {
                                t_sf = new ScreenFlash(this, m_flash_tex_count_three, "count_beep", ScreenFlashType.ZoomWithTrailAndFade, 1.0f, 2.5f);
                            }

                            m_screen_flashes.Add(t_sf);

                        }
                    }

                }

                // Take care of data sent "pregame"
                if (t_datatype == "CarType")
                {
                    //Console.WriteLine("LOL");
                    byte t_gamer_id = m_packetReader.ReadByte(); // gamer id
                    int t_car_type = m_packetReader.ReadInt32(); // car type
                    if (m_car_types.ContainsKey(t_gamer_id))
                    {
                        m_car_types[t_gamer_id] = t_car_type;
                    }
                    else
                    {
                        m_car_types.Add(t_gamer_id, t_car_type);
                    }
                }
                
            }
        }

        public void NetworkSendData(NetworkDataType p_datatype, List<Object> p_data, List<NetworkGamer> p_recipients)
        {
            if (p_datatype == NetworkDataType.SpawnPosition)
            {
                m_packetWriter.Write("SpawnPosition");
            }
            else if (p_datatype == NetworkDataType.PlayerMove)
            {
                m_packetWriter.Write("PlayerMove");
            }
            else if (p_datatype == NetworkDataType.CarType)
            {
                m_packetWriter.Write("CarType");
            }
            else if (p_datatype == NetworkDataType.CountDown)
            {
                m_packetWriter.Write("CountDown");
            }

            foreach (Object t_data in p_data)
            {
                if (t_data.GetType().ToString() == "Microsoft.Xna.Framework.Vector3")
                    m_packetWriter.Write((Vector3)t_data);
                else if (t_data.GetType().ToString() == "Microsoft.Xna.Framework.Matrix")
                    m_packetWriter.Write((Matrix)t_data);
                else if (t_data.GetType().ToString() == "System.Byte")
                    m_packetWriter.Write((Byte)t_data);
                else if (t_data.GetType().ToString() == "System.Int32")
                    m_packetWriter.Write((int)t_data);
            }

            // Send our data!
            if (p_recipients != null)
            {
                foreach (NetworkGamer t_recipient in p_recipients)
                {
                    m_networkSession.LocalGamers[0].SendData(m_packetWriter, SendDataOptions.InOrder, t_recipient);
                }
            }
            else
            {
                m_networkSession.LocalGamers[0].SendData(m_packetWriter, SendDataOptions.InOrder);
            }
        }

        public void NetworkGameStarted(object sender, GameStartedEventArgs e)
        {
            Console.WriteLine(e.ToString());

            // Init game and load world
            InitMultiplayerGame();

            // If we are the host, send out spawn locations
            if (m_networkSession.IsHost)
            {
                // Set count down data
                m_init_countdown_id = 4;
                m_init_countdown_time = 3.1f;

                // First get our own spawn point
                gameWorld.gameSpawnPoints[0].taken = true; // Take the first one, TODO: Random spawnpoints.
                List<Object> t_data_to_send = new List<object>();
                t_data_to_send.Add(m_networkSession.LocalGamers[0].Id);    // Player id
                t_data_to_send.Add(gameWorld.gameSpawnPoints[0].position); // spawn position
                t_data_to_send.Add(gameWorld.gameSpawnPoints[0].rotation); // ... and rotation
                if (m_car_types.ContainsKey(m_networkSession.LocalGamers[0].Id))
                {
                    t_data_to_send.Add(m_car_types[m_networkSession.LocalGamers[0].Id]); // car type
                }
                else
                {
                    t_data_to_send.Add(0); // car type
                }
                NetworkSendData(NetworkDataType.SpawnPosition, t_data_to_send, null);
                if (m_car_types.ContainsKey(m_networkSession.LocalGamers[0].Id))
                    gameWorld.addLocalGamePlayer(m_car_types[m_networkSession.LocalGamers[0].Id], gameWorld.gameSpawnPoints[0].position, gameWorld.gameSpawnPoints[0].rotation);
                else
                    gameWorld.addLocalGamePlayer(0, gameWorld.gameSpawnPoints[0].position, gameWorld.gameSpawnPoints[0].rotation);

                GameObject targetObject = m_gameWorld.GetObject("LocalCar");

                m_camera = new Camera(this, 
                                      new Vector3(16.94671f, 77.25067f, -110.0087f),
                                      new Vector3(-0.3810003f, 3.024002f, 0.0f),
                                      Camera.cameraState.FOLLOW,
                                      targetObject);

                // Send out spawnpoints for each gamer
                foreach (NetworkGamer t_gamer in networkSession.RemoteGamers)
                {
                    // Find available spawnpoint (just take a free one for now, TODO: Random spawnpoints.)
                    foreach (GameSpawnPoint t_spoint in gameWorld.gameSpawnPoints)
                    {
                        if (!t_spoint.taken)
                        {
                            t_spoint.taken = true;

                            // We have found a free spawn point!
                            t_data_to_send = new List<object>();
                            t_data_to_send.Add(t_gamer.Id);        // Player id
                            t_data_to_send.Add(t_spoint.position); // spawn position
                            t_data_to_send.Add(t_spoint.rotation); // ... and rotation
                            if (m_car_types.ContainsKey(t_gamer.Id))
                            {
                                t_data_to_send.Add(m_car_types[t_gamer.Id]); // car type
                            }
                            else
                            {
                                t_data_to_send.Add(0); // car type
                            }
                            NetworkSendData(NetworkDataType.SpawnPosition, t_data_to_send, null);
                            if (m_car_types.ContainsKey(t_gamer.Id))
                                gameWorld.addRemoteGamePlayer(t_gamer, m_car_types[t_gamer.Id], t_spoint.position, t_spoint.rotation);
                            else
                                gameWorld.addRemoteGamePlayer(t_gamer, 0, t_spoint.position, t_spoint.rotation);

                            // Done, continue with next player!
                            break;
                        }
                    }
                }
            }

        }

        public void NetworkGameEnded(object sender, GameEndedEventArgs e)
        {
            // TODO: Make this reason show up in a popup/infobox ingame.
            Console.WriteLine(e.ToString());
            
            m_gamestate = GameState.MenuScreen;
            Components.Clear();
            m_gameInitiated = false;
            m_gameWorld = null;
            this.IsMouseVisible = true;
        }

        public void NetworkSessionEnded(object sender, NetworkSessionEndedEventArgs e)
        {
            // TODO: Make this reason show up in a popup/infobox ingame.
            Console.WriteLine(e.EndReason.ToString());

            // Get us back to the menu!
            m_gamestate = GameState.MenuScreen;
            Components.Clear();
            m_gameInitiated = false;
            m_gameWorld = null;
            this.IsMouseVisible = true;

            // Get rid of the network session..
            networkSession.Dispose();
            networkSession = null;
        }
        #endregion Network Methods

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime p_gameTime)
        {
            // update input devices states
            UserInput.instance.update();

            // Allows the game to exit
            if (UserInput.instance.keyboardState.IsKeyDown(Keys.Escape))
            {
                this.Exit();
            }
            /*
            // Play music
            if ((currentSong == null || currentSong.IsStopped) && m_gamestate == GameState.MultiplayerGame)
            {
                currentSong = soundBank.GetCue("Song01");

                if (!currentSong.IsPlaying)
                {
                    currentSong.Play();
                }
            }

            if (currentSong != null && currentSong.IsPlaying && m_gamestate == GameState.MenuScreen)
            {
                currentSong.Stop(AudioStopOptions.Immediate);
            }*/
            /*if (!currentSong.IsPlaying)
            {
                currentSong.Play();
            }*/

            // Toggle display gamer list HUD
            if (UserInput.instance.keyboardState.IsKeyDown(Keys.Tab) &&
                UserInput.instance.prevKeyboardState.IsKeyUp(Keys.Tab))
            {
                m_show_gamer_list = !m_show_gamer_list;
            }

            // Decrease game clock
            if (m_networkSession != null && m_networkSession.SessionState == NetworkSessionState.Playing)
            {
                if (m_gameInitiated || m_playtime.CompareTo(TimeSpan.Zero) <= 0)
                    m_playtime = m_playtime.Subtract(p_gameTime.ElapsedGameTime);

                if (m_playtime.CompareTo(TimeSpan.Zero) <= 0)
                    m_gameInitiated = false;

                if (m_networkSession.IsHost &&
                (UserInput.instance.keyboardState.IsKeyDown(Keys.Back) && UserInput.instance.prevKeyboardState.IsKeyUp(Keys.Back) ||
                (m_playtime.CompareTo(new TimeSpan(0, 0, -10)) <= 0)) )
                {
                    m_networkSession.EndGame();
                    //m_networkSession.ResetReady();
                }
            }
            
            // Screen flashes
            List<ScreenFlash> t_remove = new List<ScreenFlash>();
            foreach (ScreenFlash t_sf in m_screen_flashes)
            {
                t_sf.Update(p_gameTime);
                if (t_sf.Done)
                {
                    t_remove.Add(t_sf);
                }
            }
            foreach (ScreenFlash t_sf in t_remove)
	        {
                m_screen_flashes.Remove(t_sf);
	        }
            t_remove.Clear();


            // Network update (send / recieve)
            if (m_networkSession != null)
            {
                NetworkReadData();
                
                // Send our movement data
                if (gameWorld != null && gameWorld.gamePlayer != null)
                {
                    List<Object> t_data_list = new List<object>();
                    t_data_list.Add(m_networkSession.LocalGamers[0].Id);           // Player id
                    t_data_list.Add(gameWorld.gamePlayer.carObject.body.Position); // position
                    t_data_list.Add(gameWorld.gamePlayer.carObject.body.Velocity); // velocity
                    t_data_list.Add(gameWorld.gamePlayer.carObject.body.Orientation); // orientation
                    if (m_networkSession.IsHost)
                    {
                        // We are host, send our data to everyone!
                        NetworkSendData(NetworkDataType.PlayerMove, t_data_list, null);
                    } else {
                        // We are a client, only send the data to the host. :)
                        List<NetworkGamer> t_recip = new List<NetworkGamer>();
                        t_recip.Add(m_networkSession.Host);
                        NetworkSendData(NetworkDataType.PlayerMove, t_data_list, t_recip);
                    }
                }

                // Init count down
                if (m_networkSession.IsHost)
                {
                    if (m_init_countdown_time >= 0.0f)
                    {
                        List<Object> t_data_list = new List<object>();
                        m_init_countdown_time -= (float)p_gameTime.ElapsedGameTime.Milliseconds / 1000.0f;

                        if (m_init_countdown_id == 4 && m_init_countdown_time < 3.0)
                        {                            
                            m_init_countdown_id = 3;
                            t_data_list.Add(m_init_countdown_id);
                            NetworkSendData(NetworkDataType.CountDown, t_data_list, null);

                            ScreenFlash t_sf = new ScreenFlash(this, m_flash_tex_count_three, "count_beep", ScreenFlashType.ZoomWithTrailAndFade, 1.0f, 2.5f);
                            m_screen_flashes.Add(t_sf);
                        }
                        else if (m_init_countdown_id == 3 && m_init_countdown_time < 2.0)
                        {
                            m_init_countdown_id = 2;
                            t_data_list.Add(m_init_countdown_id);
                            NetworkSendData(NetworkDataType.CountDown, t_data_list, null);

                            ScreenFlash t_sf = new ScreenFlash(this, m_flash_tex_count_two, "count_beep", ScreenFlashType.ZoomWithTrailAndFade, 1.0f, 2.5f);
                            m_screen_flashes.Add(t_sf);
                        }
                        else if (m_init_countdown_id == 2 && m_init_countdown_time < 1.0)
                        {
                            m_init_countdown_id = 1;
                            t_data_list.Add(m_init_countdown_id);
                            NetworkSendData(NetworkDataType.CountDown, t_data_list, null);

                            ScreenFlash t_sf = new ScreenFlash(this, m_flash_tex_count_one, "count_beep", ScreenFlashType.ZoomWithTrailAndFade, 1.0f, 2.5f);
                            m_screen_flashes.Add(t_sf);
                        }
                        else if (m_init_countdown_id == 1 && m_init_countdown_time <= 0.0)
                        {
                            m_init_countdown_id = 0;
                            t_data_list.Add(m_init_countdown_id);
                            NetworkSendData(NetworkDataType.CountDown, t_data_list, null);

                            ScreenFlash t_sf = new ScreenFlash(this, m_flash_tex_count_go, "go_beep", ScreenFlashType.ZoomWithTrailAndFade, 1.0f, 2.5f);
                            m_screen_flashes.Add(t_sf);
                            m_gameInitiated = true;
                        }
                    }
                }

                m_networkSession.Update();
            }
            
            //
            // update game logic(?)
            //

            if (m_gamestate == GameState.MenuScreen)
            {
                // update screens
                m_screenMainMenu.Update(p_gameTime);
            }
            else
            {
                if (m_physSystem != null)
                {
                    m_audioEngine.Update();

                    m_gameWorld.removeGameObjects();

                    // integrate physics with fixed time step and make up for "lost" time
                    m_timeStep += (float)p_gameTime.ElapsedGameTime.Ticks / TimeSpan.TicksPerSecond;
                    /*float t_timeStep = (float)p_gameTime.ElapsedGameTime.Ticks / TimeSpan.TicksPerSecond;
                    if (t_timeStep < 1.0f / 60.0f) PhysicsSystem.CurrentPhysicsSystem.Integrate(t_timeStep);
                    else PhysicsSystem.CurrentPhysicsSystem.Integrate(1.0f / 60.0f);
                    */
                    float fixedTimeStep = 1.0f / 100.0f;

                    m_iterationsMade = 0;

                    while (m_timeStep > fixedTimeStep)
                    {
                        PhysicsSystem.CurrentPhysicsSystem.Integrate(fixedTimeStep);

                        m_timeStep -= fixedTimeStep;

                        m_iterationsMade++;
                    }
                    
                    // update camera
                    m_camera.UpdateCamera(p_gameTime);
                }
            }

            // Update editor only in debug game
            if (m_gamestate == GameState.DebugGame && m_physSystem != null)
                m_formEditor.UpdateObject(m_mapEditor.selectedGameObject);

            m_gameTime = p_gameTime;

            base.Update(p_gameTime);

            if (m_gameWorld != null)
            {
                m_gameWorld.update(p_gameTime);
            }

            // Sven A - Easter Egg - Konami Code
            Keys[] t_keys = UserInput.instance.keyboardState.GetPressedKeys();
            if (t_keys.Length == 1)
            {
                Keys t_key = t_keys[0];
                if (UserInput.instance.prevKeyboardState.IsKeyUp(t_key))
                {
                    // Check current
                    bool t_correct = true;
                    for (int i = 0; i < m_ee_konamicode_i; ++i)
                    {
                        if (m_ee_konamicode[i].ToString() != m_ee_konamicode_correct[i].ToString())
                        {
                            t_correct = false;
                            m_ee_konamicode_i = 0;
                            m_ee_konamicode.Clear();
                            break;
                        }
                    }

                    if (t_correct)
                    {
                        if (m_ee_konamicode_i == 10)
                        {
                            //Console.WriteLine("OHHHHHHHHHHH YEA!!!");
                            // My little pony Easter Egg activated! :)
                            m_ee_activated = true;
                            m_ee_sweetfish_tex = Texture2D.FromFile(GraphicsDevice, "Content\\ee_sweetfish.png");
                            ScreenFlash t_sf = new ScreenFlash(this, m_ee_sweetfish_tex, "cash_register", ScreenFlashType.ZoomAndFade, 2.0f, 2.0f);
                            m_screen_flashes.Add(t_sf);
                            m_ee_konamicode_i = 0;
                            m_ee_konamicode.Clear();
                        }
                    }

                    m_ee_konamicode.Add(t_key);
                    m_ee_konamicode_i += 1;
                }
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime p_gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            if (m_gamestate == GameState.MenuScreen)
            {
                // Draw screens!
                m_screenMainMenu.Draw();

                // Draw screen flashes
                m_spriteBatch.Begin();
                foreach (ScreenFlash t_sf in m_screen_flashes)
                {
                    t_sf.Draw(m_spriteBatch);
                }
                m_spriteBatch.End();
            }
            else
            {
                m_gameWorld.draw(p_gameTime);

                if (m_gamestate == GameState.MultiplayerGame && gameWorld.gamePlayer != null)
                {
                    // If the reverse powerup is active, set reverse_powerup = true
                    

                    //////////////////////
                    // Draw HUD
                    m_spriteBatch.Begin();

                    // Draw screen flashes
                    reverse_powerup = false;
                    int t_powerup_counter = 0;
                    foreach (ScreenFlash t_sf in m_screen_flashes)
                    {
                        if (t_sf.m_type == 4)
                            reverse_powerup = true;

                        if (t_sf.animation == ScreenFlashType.PowerUpCounter)
                        {
                            t_sf.Draw(m_spriteBatch, t_powerup_counter);
                            t_powerup_counter += 1;
                        }
                        else
                            t_sf.Draw(m_spriteBatch);
                    }

                    // Draw game time
                    string t_clockstring;
                    Color t_clockcolor = Color.White;
                    if (m_playtime.CompareTo(TimeSpan.Zero) < 0)
                    {
                        t_clockstring = "0:00";
                    }
                    else
                    {
                        t_clockstring = m_playtime.Minutes.ToString() + ":";
                        if (m_playtime.Seconds < 10)
                        {
                            t_clockstring += "0";
                            if (m_playtime.Minutes == 0)
                                t_clockcolor = Color.Red;
                        }
                        t_clockstring += m_playtime.Seconds.ToString();
                    }

                    m_spriteBatch.DrawString(m_menuFontHuge, t_clockstring, new Vector2(371, 4), Color.Black);
                    m_spriteBatch.DrawString(m_menuFontHuge, t_clockstring, new Vector2(370, 3), t_clockcolor);

                    // Local score
                    m_spriteBatch.DrawString(m_menuFontBig, "Score: " + gameWorld.gamePlayer.show_points, new Vector2(21, 551), Color.Black);
                    m_spriteBatch.DrawString(m_menuFontBig, "Score: " + gameWorld.gamePlayer.show_points, new Vector2(20, 550), Color.White);


                    // Player list
                    if (m_show_gamer_list && m_playtime.CompareTo(TimeSpan.Zero) > 0)
                    {
                        int t_id = 0;
                        int t_score = 0;
                        float t_x = (float)GraphicsDevice.Viewport.Width - 230.0f, t_y = 5.0f;
                        foreach (NetworkGamer t_gamer in m_networkSession.AllGamers)
                        {
                            t_y += 23;// t_id * 23;

                            // Draw background
                            m_spriteBatch.Draw(m_gamer_list_bg, new Rectangle((int)t_x, (int)t_y, 256, 32), Color.White);

                            // Draw gamertag
                            m_spriteBatch.DrawString(m_menuFontTiny, t_gamer.Gamertag, new Vector2(t_x + 13, t_y + 3), Color.Black);
                            if (t_gamer.IsLocal)
                                m_spriteBatch.DrawString(m_menuFontTiny, t_gamer.Gamertag, new Vector2(t_x + 12, t_y + 2), Color.GreenYellow);
                            else
                                m_spriteBatch.DrawString(m_menuFontTiny, t_gamer.Gamertag, new Vector2(t_x + 12, t_y + 2), Color.White);


                            // Draw points
                            if (t_gamer.IsLocal)
                            {
                                t_score = m_gameWorld.gamePlayer.show_points;
                            }
                            else
                            {
                                foreach (GamePlayer t_player in m_gameWorld.remotePlayers)
                                {
                                    if (t_player.gamer == t_gamer)
                                    {
                                        t_score = t_player.show_points;
                                    }
                                }
                            }
                            m_spriteBatch.DrawString(m_menuFontTiny, t_score.ToString().PadLeft(7, ' '), new Vector2(t_x + 171, t_y + 3), Color.Black);
                            if (t_gamer.IsLocal)
                                m_spriteBatch.DrawString(m_menuFontTiny, t_score.ToString().PadLeft(7, ' '), new Vector2(t_x + 170, t_y + 2), Color.GreenYellow);
                            else
                                m_spriteBatch.DrawString(m_menuFontTiny, t_score.ToString().PadLeft(7, ' '), new Vector2(t_x + 170, t_y + 2), Color.White);


                            t_id++;
                        }
                    }

                    // Draw final player list
                    if (m_playtime.CompareTo(TimeSpan.Zero) <= 0)
                    {

                        // Dim screen
                        m_spriteBatch.Draw(m_blank_texture, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), new Color(Color.Black, 0.5f));

                        int t_id = 0;
                        int t_score = 0;
                        byte t_winner_id = 255;
                        int t_winner_score = -1;
                        float t_x = (float)GraphicsDevice.Viewport.Width / 2.0f - 256.0f, t_y = 80.0f;
                        foreach (NetworkGamer t_gamer in m_networkSession.AllGamers)
                        {
                            t_y += 32;// t_id * 32;

                            // Draw background
                            m_spriteBatch.Draw(m_gamer_finallist_bg, new Rectangle((int)t_x, (int)t_y, 512, 32), Color.White);

                            // Draw gamertag
                            m_spriteBatch.DrawString(m_menuFontSmall, t_gamer.Gamertag, new Vector2(t_x + 36, t_y + 5), Color.Black);
                            if (t_gamer.IsLocal)
                                m_spriteBatch.DrawString(m_menuFontSmall, t_gamer.Gamertag, new Vector2(t_x + 35, t_y + 4), Color.GreenYellow);
                            else
                                m_spriteBatch.DrawString(m_menuFontSmall, t_gamer.Gamertag, new Vector2(t_x + 35, t_y + 4), Color.White);

                            // Draw points
                            if (t_gamer.IsLocal)
                            {
                                t_score = m_gameWorld.gamePlayer.show_points;
                            }
                            else
                            {
                                foreach (GamePlayer t_player in m_gameWorld.remotePlayers)
                                {
                                    if (t_player.gamer == t_gamer)
                                    {
                                        t_score = t_player.show_points;
                                    }
                                }
                            }

                            // Find winner?
                            if (m_winner_id == 255)
                            {
                                if (t_score > t_winner_score)
                                {
                                    t_winner_score = t_score;
                                    t_winner_id = t_gamer.Id;
                                }
                            }
                            else
                            {
                                // Draw winner icon
                                if (m_winner_id == t_gamer.Id)
                                {
                                    m_spriteBatch.Draw(m_winner_icon, new Vector2(t_x + 2.0f, t_y - 1.0f), Color.White);
                                }
                            }

                            m_spriteBatch.DrawString(m_menuFontSmall, t_score.ToString().PadLeft(10, ' '), new Vector2(t_x + 424, t_y + 5), Color.Black);
                            if (t_gamer.IsLocal)
                                m_spriteBatch.DrawString(m_menuFontSmall, t_score.ToString().PadLeft(10, ' '), new Vector2(t_x + 423, t_y + 4), Color.GreenYellow);
                            else
                                m_spriteBatch.DrawString(m_menuFontSmall, t_score.ToString().PadLeft(10, ' '), new Vector2(t_x + 423, t_y + 4), Color.White);


                            t_id++;
                        }

                        if (t_winner_id != 255)
                            m_winner_id = t_winner_id;
                    }

                    // Done drawing HUD
                    m_spriteBatch.End();
                }
            }

            // Draw alpha-version information
            m_spriteBatch.Begin();
            String t_verinfo = "LloydGame 0.8.0.0 Beta";
            m_spriteBatch.DrawString(m_menuFontSmall, t_verinfo, new Vector2(575, 575), Color.Black);
            m_spriteBatch.DrawString(m_menuFontSmall, t_verinfo, new Vector2(574, 574), Color.White);
            m_spriteBatch.End();
            

            base.Draw(p_gameTime);
        }
    }
}
